package think.in.java.concurrency.PractiseOneMoreTime;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Create two Runnables,
 * one with a run( ) that starts and calls wait( ).
 * The second class should capture the reference of the first Runnable object.
 * Its run( ) should call notifyAll( ) for the first task after some number of
 * seconds have passed so that the first task can display a message. Test your classes using an Executor
 */
public class Exercise21Waiter {
    public static void main(String[] args) throws InterruptedException {
        Object monitor = new Object();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(new ThreadOne(monitor));
        executorService.execute(new ThreadOne(monitor));
        executorService.execute(new ThreadOne(monitor));
        executorService.execute(new ThreadOne(monitor));
        executorService.execute(new ThreadOne(monitor));
        Thread.sleep(1000);
        executorService.execute(new ThreadTwo(monitor));
        Thread.yield();
        Thread.sleep(5000);
        executorService.shutdown();
    }
}

class ThreadOne implements Runnable {
    private Object monitor;

    public ThreadOne(Object monitor) {
        this.monitor = monitor;
    }

    @Override
    public void run() {//shold be like this
        System.out.println(Thread.currentThread().getName() + "Thread one start");
//        while (!Thread.interrupted()) {
        try {
            synchronized(monitor){//todo note there, must be with the synchronized, as the synchronized will obtain a lock the the wait, else wait cannot get the monitor
            monitor.wait();
            }
            System.out.println(Thread.currentThread().getName() + "Thread one waited");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (IllegalMonitorStateException e) {
            System.out.println("Try wait again");
            try {
                Thread.sleep(100);
                Thread.yield();
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
        }
//        }

        System.out.println("Thread one end");
    }

    public void errorrun() {//this method is wrong
        System.out.println(Thread.currentThread().getName() + "Thread one start");
        try {

            monitor.wait();// note one thread can hold the wait lock many times, but if other thread wants to get the wait lock, a exception will thrown
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Thread one end");
    }
}

class ThreadTwo implements Runnable {
    private Object monitor;

    public ThreadTwo(Object monitor) {
        this.monitor = monitor;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "Thread two start");

        System.out.println("Thread two sleepped");
        System.out.println("Thread two notify one");
        synchronized(monitor){
            monitor.notify();
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Thread two notify all");
        synchronized(monitor){
        monitor.notifyAll();
        }


    }
}
